package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.exception.CustomException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.feign.article.IArticleClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.GreenScanService;
import com.heima.wemedia.service.WmNewsAutoScanService;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.TesseractException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {
    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Autowired
    private WmUserMapper wmUserMapper;
    @Autowired
    private WmChannelMapper wmChannelMapper;
    @Autowired
    private GreenScanService greenScanService;
    @Autowired
    private IArticleClient iArticleClient;
    @Autowired
    private FileStorageService fileStorageService; //minio
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    Tess4jClient tess4jClient;  // tess4j工具类
    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;

    /**
     * 自媒体文章自动审核
     *
     * @param id
     */
    @Override
    @Async  //标注该方法为异步方法      :底层是线程池,
    public void autoScanWmNews(Integer id) {
        //1.入参校验
        //2.数据处理根据id查询文章
        WmNews wmNews = wmNewsMapper.selectById(id);
        if (wmNews == null || StringUtils.isBlank(wmNews.getContent())) {
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        if (wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())) {
            //提交 待审核状态

            //3.获取文章的纯文本内容 与 图片内容
            Map<String, Object> map = handleTextAndImage(wmNews);

            /////先进入自管理敏感词审核,通过后再调用第三方内容接口进行审核
            boolean isSensitiveOk = handleSensitiveScan(map.get("text").toString(), wmNews);
            if (!isSensitiveOk) return;

            //4.审核文章的文本内容
            boolean isTextScanOk = scanText(map.get("text").toString(), wmNews);
            if (!isTextScanOk) return;

            //5.审核文章的图片内容
            boolean isImageScanOk = scanImage((List<String>) map.get("image"), wmNews);
            if (!isImageScanOk) return;
            //发起远程feign调用
            try {
                //6.如果审核都OK,远程调用app端的feign接口来保存文章内容
                ResponseResult result = saveArticle(wmNews);
                if (result.getCode() == 200) {
                    //远程调用相应ok
                    wmNews.setArticleId((Long) result.getData());//回填article_id
                    wmNews.setStatus(WmNews.Status.PUBLISHED.getCode());
                    wmNews.setReason("审核成功");
                    wmNewsMapper.updateById(wmNews);
                } else {
                    throw new RuntimeException("WmNewsAutoScanServiceImpl-feign远程调用-app端文章-同步保存失败!!");
                }
            } catch (RuntimeException e) {
                e.printStackTrace();
                throw new RuntimeException("WmNewsAutoScanServiceImpl-feign远程调用-app端文章-同步保存失败!!");
            }
        }
    }

    /**
     * 基于DFA算法实现 自管理敏感词库审核文章文本内容
     *
     * @param text
     * @param wmNews
     * @return
     */
    private boolean handleSensitiveScan(String text, WmNews wmNews) {
        boolean flag = true;
        if (StringUtils.isEmpty(text)) return flag;
        //1.获取所有敏感词(加入redis缓存)
        //1.1先从redis中获取所有的敏感词集合
        List<String> sensitiveWords = (List<String>) redisTemplate.opsForValue().get("SensitiveKeys");
        if (sensitiveWords == null) {
            //查询数据库,同步到redis中
            List<WmSensitive> wmSensitives =
                    wmSensitiveMapper.selectList(Wrappers.<WmSensitive>lambdaQuery().select(WmSensitive::getSensitives));
            sensitiveWords = wmSensitives.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
            redisTemplate.opsForValue().set("SensitiveKeys", sensitiveWords);//将数据库中的数据存入redis中

            //2.初始化自管理敏感词库
            SensitiveWordUtil.initMap(sensitiveWords);

            //3.校验文本内容
            Map<String, Integer> map = SensitiveWordUtil.matchWords(text);
            if (map != null&& map.size() > 0) {
                //存在违规内容
                flag = false;
                wmNews.setStatus(WmNews.Status.FAIL.getCode());
                wmNews.setReason("文章存在违规内容-审核失败");
                wmNewsMapper.updateById(wmNews);
            }

        }

        return flag;
    }

    /**
     * 发起feign远程调用app端文章微服务保存文章
     *
     * @param wmNews
     * @return
     */
    public ResponseResult saveArticle(WmNews wmNews) {
        ArticleDto dto = new ArticleDto();
        //属性拷贝
        BeanUtils.copyProperties(wmNews, dto);
        //1.参数处理
        //1.作者id 作者名称  文章布局  频道名称  文章id(wmnews有articid就给dto的id赋值)
        dto.setAuthorId(wmNews.getUserId().longValue());
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (StringUtils.isNotEmpty(wmUser.getName())) {
            dto.setAuthorName(wmUser.getName());
        }
        dto.setLayout(wmNews.getType());
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if (StringUtils.isNotEmpty(wmChannel.getName())) {
            dto.setChannelName(wmChannel.getName());
        }
        // 文章id ,如果存在文章id说是对当前文章的修改,否则是对文章的新增,携带到app端,以此来判断是做app端的文章信息还是修改
        if (wmNews.getArticleId() != null) {
            dto.setId(wmNews.getArticleId());
        }
        //2.发送feign请求
        ResponseResult responseResult = iArticleClient.saveArticle(dto);
        //3.返回响应结果
        return responseResult;
    }

    /**
     * 审核图片
     *
     * @param image
     * @param wmNews
     * @return
     */
    private boolean scanImage(List<String> image, WmNews wmNews) {
        boolean flag = true;
        //根据图片的url到minio中下载图片
        for (String url : image) {
//           //test4j----
//            try {
//                /////根据自管理敏感词 判断图片中解析的文字 使用tess4j解析图片中的文字
//                //下载minio中的图片
//                byte[] bytes = fileStorageService.downLoadFile(url);
//                InputStream in = new ByteArrayInputStream(bytes);
//                //从byte[]转换为bufferedImage
//                BufferedImage bufferedImage = ImageIO.read(in);
//                String resultContent = tess4jClient.doOCR(bufferedImage);
//                //调用自管理的文本敏感词校验
//                boolean isSensitiveOk = handleSensitiveScan(resultContent, wmNews);
//                if (!isSensitiveOk) {
//                    //未通过自管理敏感词库
//                    flag = false;
//                    break;
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//                flag = false;
//            }

            Map map = greenScanService.scanImage(url);
            if ((int) map.get("conclusionType") == WemediaConstants.BAIDU_SCAN_FAIL) {
                //审核失败
                flag = false;
                wmNews.setStatus(WmNews.Status.FAIL.getCode());
                wmNews.setReason("文章中存在违规图片-审核失败");
                wmNewsMapper.updateById(wmNews);
                break;
            }
            if ((int) map.get("conclusionType") == WemediaConstants.BAIDU_SCAN_SUSPECT) {
                //疑似
                flag = false;
                wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
                wmNews.setReason("文章中存在疑似违规图片-转人工审核");
                wmNewsMapper.updateById(wmNews);
                break;
            }
        }
        return flag;
    }


    /**
     * 审核文章中的纯文本内容,并对审核结果同步更新wmnews表的status和reason
     *
     * @param text
     * @param wmNews
     * @return
     */
    private boolean scanText(String text, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenScanService.scanText(text);
            if ((int) map.get("conclusionType") == WemediaConstants.BAIDU_SCAN_FAIL) {
                //审核失败
                flag = false;
                wmNews.setStatus(WmNews.Status.FAIL.getCode());
                wmNews.setReason("文章内容中存在违规-审核失败");
                wmNewsMapper.updateById(wmNews);

            }
            if ((int) map.get("conclusionType") == WemediaConstants.BAIDU_SCAN_SUSPECT) {
                //疑似
                flag = false;
                wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
                wmNews.setReason("文章内容中存在疑似违规内容-转人工审核");
                wmNewsMapper.updateById(wmNews);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return flag;
    }


    /**
     * 提取文章中的纯文本内容 和 图片(内容图片+封面图片)
     *
     * @param wmNews
     * @return
     */
    private Map<String, Object> handleTextAndImage(WmNews wmNews) {
        String content = wmNews.getContent();//文章内容(图片+文字)
        if (wmNews == null || StringUtils.isEmpty(content)) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();

        StringBuilder sb = new StringBuilder();//存储纯文本内容
        List<String> list = new ArrayList<>();//存储图片url
        sb.append(wmNews.getTitle());
        sb.append(wmNews.getLabels());

        //解析内容中纯文本 / 图片
        JSONArray jsonArray = JSON.parseArray(content);
        for (Object jsonstr : jsonArray) {
            JSONObject jsonObject = JSON.parseObject(jsonstr.toString());
            if (jsonObject.get("type").equals("text")) {
                //提取内容中的纯文本
                sb.append(jsonObject.get("value").toString());
            }
            if (jsonObject.get("type").equals("image")) {
                //提取内容中的图片url
                list.add(jsonObject.get("value").toString());
            }
        }
        //提取封面图片
        if (StringUtils.isNotEmpty(wmNews.getImages())) {
            list.addAll(Arrays.asList(wmNews.getImages().split(",")));
        }
        //返回结果
        map.put("text", sb.toString());
        map.put("image", list);
        return map;
    }
}
